import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

var delta;
var time;

var vs = [];
var fs = [];
var mesh = [];
var tex = [];
var mat = [];

var texture_loader = new THREE.TextureLoader();

const DOM = document.querySelector("#box");
const width = DOM.clientWidth;
const height = DOM.clientHeight;

const renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
});
renderer.setClearColor(0xb9eeff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height, false);
DOM.appendChild(renderer.domElement);

const camera = new THREE.PerspectiveCamera(
    45,
    width / height,
    0.05,
    100000
);
camera.position.set(5,5,5)
const scene = new THREE.Scene();
const pl1 = new THREE.PointLight(0xfee3b1, 2);
pl1.position.set(-20, 20, 20);
scene.add(pl1);

const controls = new OrbitControls(camera,renderer.domElement)
controls.target.set(0,0.5,0)

const clock = new THREE.Clock();

tex["smoke"] = texture_loader.load(
    ""
);

tex["fire"] = texture_loader.load(
    ""
);

tex["grass"] = texture_loader.load(
    ""
);

tex["floor"] = texture_loader.load(
    ""
);
tex["floor"].wrapS = tex["floor"].wrapT = THREE.RepeatWrapping;
tex["floor"].repeat.set(20, 20);

vs["sprite"] = `


attribute vec3 offset;
attribute vec2 scale;
attribute vec4 quaternion;
attribute float rotation;
attribute vec4 color;
attribute float blend;
attribute float texture;
uniform float time;
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;
vec3 localUpVector=vec3(0.0,1.0,0.0);


void main(){


float angle=time*rotation;
vec3 vRotated=vec3(position.x*scale.x*cos(angle)-position.y*scale.y*sin(angle),position.y*scale.y*cos(angle)+position.x*scale.x*sin(angle),position.z);


vUv=uv;
vColor=color;
vBlend=blend;
num=texture;


vec3 vPosition;




/*
vec3 vLook=normalize(offset-cameraPosition);
vec3 vRight=normalize(cross(vLook,localUpVector));
vec3 vUp=normalize(cross(vLook,vRight));
vPosition=vRight*vRotated.x+vUp*vRotated.y+vLook*vRotated.z;
*/


vec3 vLook=offset-cameraPosition;
vec3 vRight=normalize(cross(vLook,localUpVector));
vPosition=vRotated.x*vRight+vRotated.y*localUpVector+vRotated.z;



gl_Position=projectionMatrix*modelViewMatrix*vec4(vPosition+offset,1.0);


}


`;

fs["sprite"] = `


const int count=3;
uniform sampler2D map[count];
varying vec2 vUv;
varying vec4 vColor;
varying float vBlend;
varying float num;


void main(){


if(num==0.0){ gl_FragColor=texture2D(map[0],vUv)*vColor; }
else if(num==1.0){ gl_FragColor=texture2D(map[1],vUv)*vColor; }
else if(num==2.0){ gl_FragColor=texture2D(map[2],vUv)*vColor; }


gl_FragColor.rgb*=gl_FragColor.a;
gl_FragColor.a*=vBlend;


}


`;

var particles = [];

// ____________________ GRASS ____________________

var particles_grass_a = [];

particles_grass_a.push({
    offset: [10, 2, 0],
    scale: [4, 4],
    quaternion: [0, 0, 0, 4],
    rotation: 0,
    color: [1, 1, 1, 1],
    blend: 1,
    texture: 2,
});

for (var n = 0; n < 100; n++) {
    var scale = Math.random() * 0.5 + 0.5;
    particles_grass_a.push({
        offset: [Math.random() * 20 - 10, scale / 2, Math.random() * 20 - 10],
        scale: [scale, scale],
        quaternion: [0, 0, 0, 4],
        rotation: 0,
        color: [1, 1, 1, 1],
        blend: 1,
        texture: 2,
    });
}

// ____________________ SMOKE ____________________

var wind_x = 0.002;
var wind_y = 0;
var wind_z = 0;

var particles_smoke_a = [];

var particles_emmiter = [];

particles_emmiter.push({
    position: { x: -2, y: 0, z: -4 },
    radius_1: 0.02,
    radius_2: 1,
    radius_height: 5,
    add_time: 0.1,
    elapsed: 0,
    live_time_from: 7,
    live_time_to: 7.5,
    opacity_decrease: 0.008,
    rotation_from: 0.5,
    rotation_to: 1,
    speed_from: 0.005,
    speed_to: 0.01,
    scale_from: 0.2,
    scale_increase: 0.004,
    color_from: [2, 2, 2],
    color_to: [0, 0, 0],
    color_speed_from: 0.4,
    color_speed_to: 0.4,
    brightness_from: 1,
    brightness_to: 1,
    opacity: 1,
    blend: 0.8,
    texture: 1,
});

particles_emmiter.push({
    position: { x: 0, y: 0, z: -4 },
    radius_1: 0.02,
    radius_2: 1,
    radius_height: 5,
    add_time: 0.1,
    elapsed: 0,
    live_time_from: 10,
    live_time_to: 10.5,
    opacity_decrease: 0.008,
    rotation_from: 0.5,
    rotation_to: 1,
    speed_from: 0.005,
    speed_to: 0.01,
    scale_from: 0.2,
    scale_increase: 0.004,
    color_from: [0.1, 0.1, 0.1],
    color_to: [0.1, 0.1, 0.1],
    color_speed_from: 1,
    color_speed_to: 1,
    brightness_from: 1,
    brightness_to: 1,
    opacity: 1,
    blend: 1,
    texture: 0,
});

particles_emmiter.push({
    position: { x: 2, y: 0, z: -4 },
    radius_1: 0.02,
    radius_2: 0.4,
    radius_height: 5,
    add_time: 0.1,
    elapsed: 0,
    live_time_from: 4,
    live_time_to: 4.5,
    opacity_decrease: 0.004,
    rotation_from: 2,
    rotation_to: 3,
    speed_from: 0.005,
    speed_to: 0.01,
    scale_from: 0.1,
    scale_increase: 0.003,
    color_from: [1, 1, 1],
    color_to: [1, 1, 1],
    color_speed_from: 1,
    color_speed_to: 1,
    brightness_from: 1.0,
    brightness_to: 1,
    opacity: 0.4,
    blend: 0.5,
    texture: 0,
});

particles_emmiter.push({
    position: { x: 4, y: 0, z: -4 },
    radius_1: 2,
    radius_2: 2,
    radius_height: 5,
    add_time: 0.1,
    elapsed: 0,
    live_time_from: 1,
    live_time_to: 1.5,
    opacity_decrease: 0.004,
    rotation_from: 2,
    rotation_to: 3,
    speed_from: 0.005,
    speed_to: 0.01,
    scale_from: 0.0,
    scale_increase: 0.003,
    color_from: [1, 2, 1],
    color_to: [1, 1, 2],
    color_speed_from: 1,
    color_speed_to: 1,
    brightness_from: 1.0,
    brightness_to: 1,
    opacity: 0.4,
    blend: 0.7,
    texture: 0,
});

particles_emmiter.push({
    position: { x: 0, y: 1, z: 0 },
    radius_1: 0.02,
    radius_2: 1,
    radius_height: 5,
    add_time: 0.01,
    elapsed: 0,
    live_time_from: 1,
    live_time_to: 1.5,
    opacity_decrease: 0.008,
    rotation_from: 0.5,
    rotation_to: 1,
    speed_from: 0.005,
    speed_to: 0.01,
    scale_from: 0.2,
    scale_increase: 0.004,
    color_from: [2, 2, 2],
    color_to: [0, 0, 0],
    color_speed_from: 1,
    color_speed_to: 1,
    brightness_from: 1,
    brightness_to: 1,
    opacity: 1,
    blend: 0.8,
    texture: 1,
});

// ____________________ PARTICLES EMMITER EMMIT ____________________

function particles_emmiter_emmit(item) {
    var radius_1 = item.radius_1 * Math.sqrt(Math.random());
    var theta = 2 * Math.PI * Math.random();
    var x_1 = item.position.x + radius_1 * Math.cos(theta);
    var z_1 = item.position.z + radius_1 * Math.sin(theta);

    var radius_2 = item.radius_2 * Math.sqrt(Math.random());
    var theta = 2 * Math.PI * Math.random();
    var x_2 = x_1 + radius_2 * Math.cos(theta);
    var z_2 = z_1 + radius_2 * Math.sin(theta);

    let direction_x = x_2 - x_1;
    let direction_y = item.radius_height;
    let direction_z = z_2 - z_1;

    var speed =
        Math.random() * (item.speed_to - item.speed_from) + item.speed_from;

    var divide =
        (1 /
            Math.sqrt(
                direction_x * direction_x +
                    direction_y * direction_y +
                    direction_z * direction_z
            )) *
        speed;
    direction_x *= divide;
    direction_y *= divide;
    direction_z *= divide;

    var brightness =
        Math.random() * (item.brightness_to - item.brightness_from) +
        item.brightness_from;

    particles_smoke_a.push({
        offset: [x_1, item.position.y, z_1],
        scale: [item.scale_from, item.scale_from],
        quaternion: [direction_x, direction_y, direction_z, 3],
        rotation:
            Math.random() * (item.rotation_to - item.rotation_from) +
            item.rotation_from,
        color: [1, 1, 1, item.opacity],
        blend: item.blend,
        texture: item.texture,
        live:
            Math.random() * (item.live_time_to - item.live_time_from) +
            item.live_time_from,
        scale_increase: item.scale_increase,
        opacity_decrease: item.opacity_decrease,
        color_from: [
            item.color_from[0] * brightness,
            item.color_from[1] * brightness,
            item.color_from[2] * brightness,
        ],
        color_to: [
            item.color_to[0] * brightness,
            item.color_to[1] * brightness,
            item.color_to[2] * brightness,
        ],
        color_speed:
            Math.random() * (item.color_speed_to - item.color_speed_from) +
            item.color_speed_from,
        color_pr: 0,
    });
}

// ____________________ PERTICLES EMMITER UPDATE ____________________

function particles_emmiter_update() {
    var item = particles_emmiter[4].position;
    item.x = Math.sin(time / 1) * 4;
    item.z = Math.cos(time / 1) * 4;

    var max = particles_emmiter.length;

    for (var n = 0; n < max; n++) {
        var item = particles_emmiter[n];

        var add = 0;

        item.elapsed += delta;
        add = Math.floor(item.elapsed / item.add_time);
        item.elapsed -= add * item.add_time;
        if (add > (0.016 / item.add_time) * 60 * 1) {
            item.elapsed = 0;
            add = 0;
        }

        while (add--) {
            particles_emmiter_emmit(item);
        }
    }

    var max = particles_smoke_a.length;
    var alive = new Array(max);
    var i = 0;

    for (var j = 0; j < max; j++) {
        var item = particles_smoke_a[j];

        if (item.color_pr < 1) {
            var color_r =
                item.color_from[0] +
                (item.color_to[0] - item.color_from[0]) * item.color_pr;
            var color_g =
                item.color_from[1] +
                (item.color_to[0] - item.color_from[1]) * item.color_pr;
            var color_b =
                item.color_from[1] +
                (item.color_to[0] - item.color_from[2]) * item.color_pr;
            item.color_pr += delta * item.color_speed;
            item.color[0] = color_r;
            item.color[1] = color_g;
            item.color[2] = color_b;
        } else {
            item.color[0] = item.color_to[0];
            item.color[1] = item.color_to[1];
            item.color[2] = item.color_to[2];
        }

        item.offset[0] += item.quaternion[0] + wind_x;
        item.offset[1] += item.quaternion[1] + wind_y;
        item.offset[2] += item.quaternion[2] + wind_z;
        item.scale[0] += item.scale_increase;
        item.scale[1] += item.scale_increase;

        if (item.live > 0) {
            item.live -= delta;
        } else {
            item.color[3] -= item.opacity_decrease;
        }
        if (item.color[3] > 0) {
            alive[i] = item;
            i++;
        }
    }

    alive.length = i;
    particles_smoke_a = alive;
}

// ____________________ PARTICLES UPDATE ____________________

function particles_update() {
    particles_emmiter_update();

    particles = [];

    var max_1 = particles_smoke_a.length;
    particles.length = max_1;
    for (var n = 0; n < max_1; n++) {
        particles[n] = particles_smoke_a[n];
    }

    var max_2 = max_1 + particles_grass_a.length;
    particles.length = max_2;
    var i = 0;
    for (var n = max_1; n < max_2; n++) {
        particles[n] = particles_grass_a[i];
        i++;
    }

    var count = particles.length;
    var item = camera.position;
    var x = item.x;
    var y = item.y;
    var z = item.z;

    for (var n = 0; n < count; n++) {
        var item = particles[n].offset;
        particles[n].d = Math.sqrt(
            Math.pow(x - item[0], 2) +
                Math.pow(y - item[1], 2) +
                Math.pow(z - item[2], 2)
        );
    }

    particles.sort((a, b) => b.d - a.d);

    var offset = new Float32Array(count * 3);
    var scale = new Float32Array(count * 2);
    var quaternion = new Float32Array(count * 4);
    var rotation = new Float32Array(count);
    var color = new Float32Array(count * 4);
    var blend = new Float32Array(count);
    var texture = new Float32Array(count);

    for (var n = 0; n < count; n++) {
        // 1 VALUE
        var item = particles[n];
        rotation[n] = item.rotation;
        texture[n] = item.texture;
        blend[n] = item.blend;

        // 2 VALUE
        var p = n * 2;
        var one = p + 1;
        var i_scale = item.scale;
        scale[p] = i_scale[0];
        scale[one] = i_scale[1];

        // 3 VALUE
        var p = n * 3;
        var one = p + 1;
        var two = p + 2;
        var i_offset = item.offset;
        offset[p] = i_offset[0];
        offset[one] = i_offset[1];
        offset[two] = i_offset[2];

        // 4 VALUE
        var p = n * 4;
        var one = p + 1;
        var two = p + 2;
        var three = p + 3;
        var i_color = item.color;
        color[p] = i_color[0];
        color[one] = i_color[1];
        color[two] = i_color[2];
        color[three] = i_color[3];
        var i_quaternion = item.quaternion;
        quaternion[p] = i_quaternion[0];
        quaternion[one] = i_quaternion[1];
        quaternion[two] = i_quaternion[2];
        quaternion[three] = i_quaternion[3];
    }

    var item = mesh["sprite"].geometry.attributes;
    item.offset = new THREE.InstancedBufferAttribute(offset, 3).setUsage(
        THREE.DynamicDrawUsage
    );
    item.scale = new THREE.InstancedBufferAttribute(scale, 2).setUsage(
        THREE.DynamicDrawUsage
    );
    item.quaternion = new THREE.InstancedBufferAttribute(
        quaternion,
        4
    ).setUsage(THREE.DynamicDrawUsage);
    item.rotation = new THREE.InstancedBufferAttribute(
        rotation,
        1
    ).setUsage(THREE.DynamicDrawUsage);
    item.color = new THREE.InstancedBufferAttribute(color, 4).setUsage(
        THREE.DynamicDrawUsage
    );
    item.blend = new THREE.InstancedBufferAttribute(blend, 1).setUsage(
        THREE.DynamicDrawUsage
    );
    item.texture = new THREE.InstancedBufferAttribute(texture, 1).setUsage(
        THREE.DynamicDrawUsage
    );

    mesh["sprite"].geometry._maxInstanceCount = count;
}

var geometry = new THREE.InstancedBufferGeometry();
geometry.setAttribute(
    "position",
    new THREE.Float32BufferAttribute(
        new Float32Array([
            -0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0,
            -0.5, -0.5, 0,
        ]),
        3
    )
);
geometry.setAttribute(
    "uv",
    new THREE.Float32BufferAttribute(
        new Float32Array([0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0]),
        2
    )
);
geometry.setAttribute(
    "offset",
    new THREE.InstancedBufferAttribute(new Float32Array(), 3)
);
geometry.setAttribute(
    "scale",
    new THREE.InstancedBufferAttribute(new Float32Array(), 2)
);
geometry.setAttribute(
    "quaternion",
    new THREE.InstancedBufferAttribute(new Float32Array(), 4)
);
geometry.setAttribute(
    "rotation",
    new THREE.InstancedBufferAttribute(new Float32Array(), 1)
);
geometry.setAttribute(
    "color",
    new THREE.InstancedBufferAttribute(new Float32Array(), 4)
);
geometry.setAttribute(
    "blend",
    new THREE.InstancedBufferAttribute(new Float32Array(), 1)
);
geometry.setAttribute(
    "texture",
    new THREE.InstancedBufferAttribute(new Float32Array(), 1)
);

mat["sprite"] = new THREE.ShaderMaterial({
    uniforms: {
        map: { value: [tex["smoke"], tex["fire"], tex["grass"]] },
        time: { value: 0 },
    },
    vertexShader: vs["sprite"],
    fragmentShader: fs["sprite"],
    //side:THREE.DoubleSide,
    transparent: true,
    depthWrite: false,
    blending: THREE.CustomBlending,
    blendEquation: THREE.AddEquation,
    blendSrc: THREE.OneFactor,
    blendDst: THREE.OneMinusSrcAlphaFactor,
});

mesh["sprite"] = new THREE.Mesh(geometry, mat["sprite"]);
mesh["sprite"].frustumCulled = false;
mesh["sprite"].matrixAutoUpdate = false;
mesh["sprite"].updateMatrixWorld = function () {};
scene.add(mesh["sprite"]);

mat["floor"] = new THREE.MeshStandardMaterial({
    map: tex["floor"],
    bumpMap: tex["floor"],
    bumpScale: 0.01,
    metalness: 0.16,
});

mesh["floor"] = new THREE.Mesh(
    new THREE.BoxGeometry(20, 1, 20),
    mat["floor"]
);
mesh["floor"].position.y = -0.5;
scene.add(mesh["floor"]);

scene.add(new THREE.AmbientLight(0xffffff, 3));

var update_time = 0;

function loop() {
    requestAnimationFrame(loop);
    delta = clock.getDelta();
    time = clock.elapsedTime.toFixed(2);

    var started = performance.now();
    particles_update();

    update_time -= delta;

    mat["sprite"].uniforms.time.value = clock.elapsedTime;

    renderer.render(scene, camera);
}

loop();